# Copyright 2011-2011 by iNeed,Inc. All Rights Reserved.
#-*- encoding:utf-8 -*-
__author__  = "Number Huang<huang.bafeng@iNeed.com>"
__status__  = "developing"
__version__ = "0.1108.13.1"

import sys, os, time, string, message,database
from common import *
import sock_asyncore,gridManager
import json
from googlemaps import GoogleMaps
if configall.GAMETEST:
    import game



TEST_PUBLISH=1
if TEST_PUBLISH:
    NEXT_AVAILABLE_TIME_BEFORE_CA    = 5
    NEXT_AVAILABLE_TIME_IF_REJECT_CA = 10 #10 sec is just for test
    GAP_BEFORE_NEXT_ORDER   = 10#10 sec is just for test,after getting one order,if the departTime-curTime>GAP_BEFORE_NEXT_ORDER ,the driver can get next service in the gap 
    RECHOOSE_INTERVAL       = 5 #@ for test now, it should be great than 30 seconds in real world
    RECHOOSE_INTERVAL_SHORT = 1
    PublishSelectorInterval = 1 #@5 is for test, normal should be great than 30s
    TEST_TIMEOUT_MATCH      = 0
else:
     NEXT_AVAILABLE_TIME_IF_BEFORE_CA = 300
     NEXT_AVAILABLE_TIME_IF_REJECT_CA = 900 
     GAP_BEFORE_NEXT_ORDER   = 900
     RECHOOSE_INTERVAL       = 30 
     RECHOOSE_INTERVAL_SHORT = 5
     PublishSelectorInterval = 5 
     TEST_TIMEOUT_MATCH      = 0
DRIVERID_IDX=0 
POOLINGID_IDX=1

BENCH_KM_FORCHOOSE = 6 #TODO,herein, set 6KM as the bench
DEPART_GRIDRANGE="departGridRange"
ARRIVE_LATEST="arriveLatestTime"

MATCH_INTERVAL=1 # second

        
def getMatchEngine(owner):
    if not configall.matchEngine:
        configall.matchEngine = PublishMatchEngine(owner)
    return  configall.matchEngine    

def sortByRejectCount(item1,item2):
    return cmp(item1.rejectCount,item2.rejectCount)
             
class PublishMatchEngine(Task):
    """
    1，
    every PublishSelectorInterval,fetch publish from DB who is near executeTime and append to the queue
    dispatch to one publishMatcher
    2，
    execute the long term and global matching
    """
    def __init__(self,owner):
        super(PublishMatchEngine,self).__init__(owner,"publishMatchEngine",MATCH_INTERVAL,ALWAYS_RUN)
        #self.matchEngine = owner.taskEngine #@TODO it may need to create a new engine for
        self.db    = database.CreateDB(self)
        self.owner = owner
        self.gridManager  = gridManager.gridManagerFactory()
        self.offerDict  ={}#timeIdx: offerQueue
        self.requestDict={}#timeIdx: requestQueue
        self.lock   = threading.Condition()
        self.log = getLogger("iNeed.Center.match",os.path.join(CENTER_LOG_PATH,"matching.txt"))
        
    def _select(self):
        curTime = time.time()
        condition = {"execTime<":curTime,"status=":ESS_PUBLISHED}
        resultList = self.db.fetchall("taxiservice",condition)
        for service in resultList:
            phone = database.getItem("taxiservice","phone",service)
            session = self.owner.msgProxy.getSessionByPhone(phone)
            if session:
                paramDict=database.getAllItem("taxiservice",service)
                request = PublishMatcher(self,session,paramDict)
                #self.matchEngine.addTask(request,session)
                #idx = configall.getTimeIdx(request.departTime)
                #request.setTimeIdx(idx)
                #TODO: we need a strategy to assign the request to a grid range, rather than only one grid
                request.grid.addRequest(request)
                self.addRequest(request.timeIdx,request)
                #self.db.update("taxiservice",{"status":ESS_MATCHING},{"pubID":service[0]})
                
            else:
                self.db.delete("taxiservice",{"pubID":service[0]})#TODO, why we delete it?
        
    def addOffer(self,idx,offer):
        with AutoLock(self,self.lock):
            if not self.offerDict.has_key(idx):
                self.offerDict[idx]=[]
            self.offerDict[idx].append(offer)
            self._dumpOffer(offer)
    
    def addRequest(self,idx,request):
        with AutoLock(self,self.lock):
            if not self.requestDict.has_key(idx):
                self.requestDict[idx]=[]
            self.requestDict[idx].append(request)
            self._dumpRequest(request)     
    
    def deleteRequest(self,idx,pubID):
        #with AutoLock(self,self.lock):
         try:
             queue = self.requestDict.get(idx)
             if queue:
                 for item in queue:
                     if item.pubID==pubID:
                         item.endTask() 
                         item.grid.deleteRequest(item)
                         queue.remove(item)
                         self.log.debug("idx=%d,pubId=%d"%(idx,pubID))
         except:
             self.log.debug("fail_idx=%d,pubId=%d"%(idx,pubID))
             
    def _dumpRequest(self,request):
        self.log.debug("@!%d,request_%d: %s,idx=%d,num=%d,from %s to %s"%(configall.getTimeIdx(),request.pubID,request.id,request.timeIdx,request.passengerNum,\
                                                                               str(request.gridIdx),\
                                                                               str(request.gridIdx2)))
        
    def _dumpOffer(self,offer):
        
        curGrid = offer.curGrid.getRowCol()
        if offer.destGrid:
            destGrid= offer.destGrid.getRowCol()
        else:
            destGrid="anywhere"
        
        self.log.debug("@!%d,offer: %s,idx=%d,seats=%d,cur=%s,dest=%s"%(configall.getTimeIdx(),offer.id,offer.timeIdx,offer.seats,str(curGrid),str(destGrid)))
    def run(self):
        """
        select the publish and create the matcher then put to the matchEngine
        """
        
        self.matching()    
    
    def matching(self):
        """
        currently, serve the offer one by one
        TODO: a parall solution to work for prioritized offer by urgent&important
        """
        beginTime = time.time()
        self._select()
        keys = self.offerDict.keys()
        keys.sort()
        if keys:
            for key in keys:
                with AutoLock(self,self.lock):
                    offerQueue = self.offerDict[key]
                    offerQueue.sort(sortByRejectCount) 
                    if offerQueue:#the decision is needed because of the possible clean   
                        for offer in offerQueue:
                           if offer.getStatus()!=EDC_CLOSED: 
                               requests = offer.curGrid.requestQueue
                               self.matchOne(requests,offer)
                           else:
                               try:
                                   offer.curGrid.deleteOffer(offer)
                                   offerQueue.remove(offer)
                               except:
                                   
                                   self.log.exception("time=%d,grid=(%d,%d)"%(offer.timeIdx,offer.curGrid.row,offer.curGrid.col)) 
                self._select()    
        self._clean(time.time())             
            
    
    
    
    def _clean(self,time1):
        """
        clean the requests/offers that are timeout
        """
        #clean the queue  
        curTimeIdx = configall.getTimeIdx(time1)
        """
        dict1 = self.offerDict
        keys = dict1.keys()
        keys.sort()
        for key in keys:
            if key < curTimeIdx:
                queue = dict1[key]
                for item in  queue:
                    item.curGrid.deleteOffer(item)
                del dict1[key]
        """
        dict1 = self.requestDict
        keys = dict1.keys()
        keys.sort()
        for key in keys:
            if key < curTimeIdx:
                queue = dict1[key]
                for item in  queue:
                    if item.status==EPS_WAIT_CANDIDATES:
                        item.processTimeout()
                        item.grid.deleteRequest(item)
                    
                del dict1[key]
                
    def matchOne(self,requests,driver):
        """
        TODO: need a strategy to handle the priority, currently FIFO,and poolingGroup first,
        """ 
        idx     = driver.timeIdx
        seats   = driver.seats
        #check poolingorder first
        careporter = Reporter(None,self._handleCAResponse,CA_TIMEOUT,extraParam={"dc":driver})
        if requests.poolingGroupQueue.has_key(idx):
            for pg in requests.poolingGroupQueue[idx]:
                if len(pg.requestList):
                    request = pg.mainRequest
                    extraInfo={EDR_POOLINGGROUP: pg}
                    if request.status==EPS_WAIT_CANDIDATES and seats>=pg.number:
                        request.checkAvailability(driver.id,extraInfo,careporter)
        #
        if requests.queue.has_key(idx):#empty taxi
            for request in requests.queue[idx]:
                if request.status==EPS_WAIT_CANDIDATES and seats>=request.passengerNum:
                    if driver.destGrid and request.type&EST_POOLING:
                        dest1 = driver.destGrid.getRowCol()
                        dest2 = request.gridIdx2
                        
                        if self._isDestMatch(dest1, dest2):
                            
                            extraInfo ={EDR_POOLINGID:driver.poolingID}
                            request.checkAvailability(driver.id,extraInfo,careporter)
                            break
                    #try to call non-pooling taxi
                    request.checkAvailability(driver.id,reporter=careporter)#first passenger
            
    def _handleCAResponse(self,retCode,retInfo,extraInfo): 
        if retCode!=EN_OK:
            driver = extraInfo["dc"]
            driver.setRejectCount()
            
    def _isDestMatch(self,dest1,dest2):
            
            limit = 1
            top,left,bot,right = self.gridManager.getAdjacentGridsRange(dest2[0],dest2[1],limit) 
            row,col = dest1 
            if row>=top and row<=bot and col>=left and col<=right:
                return True
            else:
                return False         
class PublishMatcher(object):
    """
        1,basic flow:
       

        checkAvailabilityAsync
            if ok:
                confirmorder1Async

                endTask
            else:
                ReRunTask()

        2, timeout mechanism
        The transaction includes
        2.1 checkAvailability (1 minute)
        2.2 confirmOrder (2 minute)
        2.2.1 CD-1 minute

        3, chooseLongTermDriver
        create a ltRequest and add to the ltRequestMap of the grid
        It contains a call-back function to get the result,which has a map structure
        {driverID:id,poolingID:id}
        4, chooseShortTermDriver by grid
    """

    def __init__(self,matchEngine,session,paramDict):
        """
        preProcess the publish,like calculating fee/routing/estimatedArriveTime
        """  
        self.matchEngine = matchEngine
        #self.log   = owner.log
        self.paramDict    = paramDict
        self.pubID = self.paramDict["pubID"]
        if TEST_TIMEOUT_MATCH:
            self.timeout = TEST_TIMEOUT_MATCH
        else:
            self.timeout = self.paramDict["timeout"]
        self.begin = time.time()
        
        
        self.log = getLogger("iNeed.publish.%d"%self.pubID,os.path.join(PUBLISH_LOG_PATH,"%d.txt"%self.pubID))
        session.log = self.log #this is to collect all the session related logs
        self.type      = self.paramDict["type"]
        self.session   = session
        self.gmaps     = GoogleMaps()
        self.db        = database.CreateDB(self)
        
        self.peer = self.session.getPeer()#{"passenger": (self.session.peer.getPhone(),self.session, self.session.peer.getIP()[0],self.session.peer.getListenPort())} #phone, session,ip,port
        self.id   = self.peer.getPhone()
        self.activeDriver = None
        self.passengerInfo = self._getPassengerInfo(self.peer.getPhone())
        self.status = EPS_WAIT_CANDIDATES
        self.serviceInfo = "pubID: %d\r\n"%self.pubID
        
        
        self.standbyDriverList = [] #this is for future use,the first drvier can not offer service somehow, then choose standbyDriverList

        
        self.gridManager  = gridManager.gridManagerFactory()
        self.grid         = self.gridManager.getTheGrid(self.paramDict['x'],self.paramDict['y'])
        self.gridIdx      = self.grid.getRowCol()
        self.grid2        = self.gridManager.getTheGrid(self.paramDict['x2'],self.paramDict['y2'])
        self.gridIdx2     = self.grid2.getRowCol()
        self._preProcess((self.paramDict["x"],self.paramDict["y"],self.paramDict["address"]),(self.paramDict["x2"],self.paramDict["y2"],self.paramDict["address2"]))
        
        self.destTime   = self.paramDict["departureTime"] + self.refTime
        self.departTime = self.paramDict["departureTime"]
        self.timeIdx    = configall.getTimeIdx(self.departTime)
        self.passengerNum = self.paramDict["number"]
        self._createServiceInfo()
        
        estimateInfo={"status":ESS_MATCHING,
                      "destinationTime":self.destTime,
                      "refFee":self.refFee,
                      "refTime":self.refTime,
                      "refRouting":self.refRouting,
                      "refRouting2":self.refRouting2,
                      "gridx":self.gridIdx[0],
                      "gridx2":self.gridIdx2[0],
                      "gridy":self.gridIdx[1],
                      "gridy2":self.gridIdx2[1]}
        self.db.update("taxiservice",estimateInfo,{"pubID":self.pubID})
        self.log.debug("the gridpos(%d,%d)->(%d,%d)"%(self.paramDict['x'],self.paramDict['y'],self.grid.getRowCol()[0],self.grid.getRowCol()[1]))
        self.lock   = threading.Condition()
        self.poolingID = None
        self.forceExit = False
    
        
        
    def _calculateFee(self,distance,location="shanghai"): 
        """
        @TODO: only check the Shanghai fee now
        """  
        if  location=="shanghai":
            feeStart = 12
            kmStart  = 3
            feePerKM1 = 2.4 #less than 10KM
            feePerKM2 = 4.2 #great than 10KM
            kmFee2  = 10
            totalKM = distance/1000+1
            
            if totalKM-kmFee2>0:
                fee2 =(totalKM-kmFee2)*feePerKM2
                fee1 = feePerKM1*(kmFee2-kmStart)
            else:
                fee2 = 0
                fee1 = feePerKM1*(totalKM-kmStart)
            t = int(feeStart + fee1 + fee2+0.5)
            return t
        
    def _preProcess(self,depart,dest):
       
        if not configall.GAMETEST:
            start = u"上海"+ depart[2] #TODO: needs a solution to detect the 
            end   = u"上海"+ dest[2]
            d2 = self.gmaps.directions(start.encode('utf-8'), end.encode('utf-8'))
            #lat,lng = gmaps.address_to_latlng(address)
            if d2["status"]=="OK":
                route = d2["routes"][0]
                legs = route["legs"][0]
                distance = legs["distance"]["value"]
                self.refTime = legs["duration"]["value"]
                self.refRouting = u"全长 %d公里<br>"%(distance/1000+1)
                self.refFee = self._calculateFee(distance)
                scale = configall.LngLatSCALE
                #@TODO: update the info to DB
                departureLocation   = (legs["start_location"]["lat"]*scale,legs["start_location"]["lng"]*scale,legs["start_address"])
                #destinationLocation = (legs["end_location"]["lat"]*scale,legs["end_location"]["lng"]*scale,legs["end_address"])
                stepsList = [(item["html_instructions"],item["end_location"],item["duration"]["value"]) for item in legs["steps"]]
                routingList=[]
                for step in stepsList:
                    self.refRouting += "%s<br>"%step[0]
                    routingList.append((step[1],step[2]))
                self.refRouting2 = self.gridManager.getGridsRouting(routingList,self.gridIdx,departureLocation[:2])
                #print getUStr(self.refRouting)   
                self.refDistance = distance
        else:#simulating 
                
                dx = self.paramDict["x2"] - self.paramDict["x"]
                dy = self.paramDict["y2"] - self.paramDict["y"]
                self.refDistance= math.sqrt((dx * dx) + (dy * dy))
                self.refFee     = int(self.refDistance*7*0.0024)
                self.refTime    = int(self.refDistance/game.SPEED*1.2)
                self.refRouting2 = self.gridManager.getGridsRoutingIn2Points((self.paramDict["x"],self.paramDict["y"]),(self.paramDict["x2"],self.paramDict["y2"]),self.gridIdx)    
                self.refRouting = u"全长 %d (%f)公里  %s"%(self.refDistance,self.refDistance*7/1000,self.refRouting2)
        self.log.debug("ref2:%s"%self.refRouting2)
        
    def _createServiceInfo(self):
        
        #filter=["timeout","status","address","x","y","address2","x2","y2"]
        depart = (self.paramDict["x"],self.paramDict["y"],self.paramDict["address"])
        dest   = (self.paramDict["x2"],self.paramDict["y2"],self.paramDict["address2"])
        departureLocation   = "departureLocation: (%d,%d,'%s')"%depart
        destinationLocation = "destinationLocation: (%d,%d,'%s')"%dest
        
        #price = "priceRange:(%d,%d)"%self._calcualteFee(depart,dest)
        self.serviceInfo = "pubID: %d"%self.pubID + "\r\n"
        self.serviceInfo +=  "number: %d\r\n"%self.passengerNum
        self.serviceInfo +=  departureLocation +"\r\n"
        self.serviceInfo +=  "departureTime: " + getLocalTime(self.paramDict["departureTime"]) +"\r\n"
        self.serviceInfo +=  destinationLocation +"\r\n"
        self.serviceInfo +=  "destinationTime: " + getLocalTime(self.destTime) +"\r\n"
        self.serviceInfo +=  "refRouting: %s\r\n"%self.refRouting
        self.serviceInfo +=  "refFee: %d\r\n"%self.refFee
        self.serviceInfo +=  "refTime: %d\r\n"%self.refTime
        if self.type&EST_POOLING:
            self.serviceInfo +=  "%s: yes\r\n"%CSTR3_ENABLEPOOLING
        
        
    def processTimeout(self):
        """
        possible timeout:
        1, there is no candidate in the timeout set
        2, network issue
        """

        self.db.delete("taxiservice",{"pubID":self.pubID})
        params = "pubID: %d\r\n"%self.pubID
        params +="reason: timeout\r\n"
        flag = getSpecialFlag(CSTR_OC)
        self._sendRequest(self.peer,CSTR_OC,NonReporter,params,flag) #just send a test, and no need to handle the response because the task has exited


    def endTask(self,updateDict=None):
        
        if updateDict:
            self.db.update("taxiservice",updateDict,{"pubID":self.pubID})
        self.status = EPS_CLOSED
        self.forceExit = True
    def _getDriverInfo(self,phone):

        #TBD: join two table
        condition={"phone=":phone}
        result = self.db.fetchall("user",condition)[0]
        params = "driverName: %s\r\n"%result[2]
        params += "driverGender: %d\r\n"%result[4]
        params += "driverID: %s\r\n"%phone
        #params += "peerAge: %d\r\n"%result[5]
        result = self.db.fetchall("driver",condition)[0]
        params += "licenseID: %s\r\n"%result[0]
        params += "company: %s\r\n"%result[1]
        params += "carID: %s\r\n"%result[2]
        params += "carBrand: %s\r\n"%result[3]
        params += "carColor: %s\r\n"%result[4]
        #params += "credict: %d\r\n"%result[5]
        return params

    def _getPassengerInfo(self,phone):
        
        condition={"phone=":phone}
        result = self.db.fetchall("user",condition)[0]
        params = "passengerID: %s\r\n"%phone
        params += "passengerName: %s\r\n"%result[2]
        params += "passengerGender: %d\r\n"%result[4]
        #params += "peerAge: %d\r\n"%result[5]
        return params
    
        
    def confirmOrder(self,phone,poolingID=None,reporter1=None):
       
       self.driverPhone = phone
       session  = self.session.manager.getSessionByPhone(phone)
       driver   = session.getPeer()
       driverInfo = self._getDriverInfo(phone)
       ipInfo     = "driverIP: %s:%s\r\n"%(driver.getIP()[0],driver.getListenPort())
       extraParam =  self.serviceInfo + driverInfo + ipInfo
       
       if poolingID:
            extraParam +="poolingID: %d\r\n"%poolingID
       self.orderParam = extraParam# this would be reused in CD
       self.log.debug("begin CO %d_%s_%s"%(self.pubID,phone,self.peer.getPhone()))     
       reporter = Reporter(reporter1,self._handleConfirmOrderResponse,CO_TIMEOUT)
       self._sendRequest(self.peer,CSTR_CO,reporter,extraParam)

    def _createOrder(self):
        
       paramsDict={}
       paramsDict["driverID"]   =   self.driverPhone
       paramsDict["passengerID"]=   self.peer.getPhone()
       paramsDict["pubID"]      =   self.pubID
       paramsDict["status"]     =   EOS_CREATED
       paramsDict["type"]       =   self.paramDict["type"]
       paramsDict["number"]     =   self.paramDict["number"] 
       paramsDict["createTime"] =   time.time() 
       self.db.insert("order1",paramsDict)
       
    def _handleConfirmOrderResponse(self,retCode,retInfo,extraInfo=None):
          #Anyway, let's end this task no matter
          self.log.debug("end CO_%d,(%d,%s)"%(self.pubID,retCode,retInfo))
          if True:#retCode==EN_OK:
              if self.activeDriver:
                  self._confirmWithDriver(self.activeDriver)
              self._createOrder()
              
              updateDict={"status":ESS_CONFIRMED,"driverID":self.driverPhone}
          else:#cancel or timeout,this is not used so far
              self._processCancelOrder()
              updateDict={"status":ESS_CLOSED}
          self.endTask(updateDict) 
            
    def _processCancelOrder(self):
        """
        TODO:notify the driver that the order is canceled
        """
        self.log.debug("begin OC,%s"%self.peer.getPhone())
        reporter = Reporter(None,self._handleCancelOrderResponse,OC_TIMEOUT)
        params ="reason: cancel by peer\r\n"
        params+="pubID: %d\r\n"%self.pubID
        self._sendRequest(self.peer,CSTR_OC,reporter,params)
    
    def _handleCancelOrderResponse(self,retCode,retInfo,extraInfo=None):
        pass
    
    def _confirmWithDriver(self,peer):
        #session = peer.getSession()
        self.log.debug("begin CD,(%s,%s)"%(self.peer.getPhone(),peer.getPhone()))
        reporter = Reporter(None,self._handleConfirmDriverResponse,CD_TIMEOUT)
        params="pubID: %d\r\n"%self.pubID
        self._sendRequest(peer,CSTR_CD,reporter,params)
        
    
    def _handleConfirmDriverResponse(self,retCode,retInfo,extraInfo=None):
          """
          confirm driver is to notify the driver the order is done, it is not 
          necessary to confirm it again.
          
          """
          #status = ESS_CLOSED #error or timeout or reject
          self.log.debug("end CD_%d,(%d,%s)"%(self.pubID,retCode,retInfo))
          
          
    def _handleCheckAvailabilityResponse(self,retCode,retInfo,extraInfo):
        
        if self.forceExit:#it is possible that the passenger unpublish the order
            return
        session = extraInfo["session"]
        driver = session.getPeer()
        driverPhone = driver.getPhone()
        
        self.log.debug("end CA_%s"%driverPhone)
        driver.setOnCall(INVALID_PUBID) 
        if retCode==EN_OK:
            self.status = EPS_CONFIRMORDER            
            self.activeDriver = driver
            self.confirmOrder(driverPhone,self.poolingID)
            if self.poolingGroup:
                    
                for request in self.poolingGroup.requestList:
                    #extraParams+="subID: %d\r\n"%request.pubID
                    
                    request.confirmOrder(driverPhone,poolingID=self.pubID)
                    request.setPublishStatus(EPS_CONFIRMORDER)
            """
            if self.search==self._chooseShortTermDriver:#for short term,disable the CA until the transaction done
                 time1 =  self.refTime
                 if time1==0:
                     time1 = configall.SHORTTIME_LIMIT
                 self._unactiveTheDriver(session.getPeer(), self.paramDict["departureTime"] + time1, "shortTermOrder done")
            """
        else:
              self.status = EPS_WAIT_CANDIDATES
              self._processUnavailableDriver(extraInfo['session'])
              
        
        
    
    def _unactiveTheDriver(self,peer,time1,reason="unknown"):
        """
        TODO: 
        """
        #peer.setNextTime(time1)
        #self.log.debug("set_%s_NextTime_%s because %s"%(peer.getPhone(),getLocalTime(time1),reason))         
    
    def _processUnavailableDriver(self,session):
        '''
        This policy currently is simple: for any unavailable driver, just postpone its available time
        NEXT_AVAILABLE_TIME. Thus, the driver will not be searched in the period.
        TODO: move the logic to the client
        '''
        self._unactiveTheDriver(session.getPeer(), time.time() + NEXT_AVAILABLE_TIME_IF_REJECT_CA, "reject")
        
        

    def _processRejectOrderDriver(self,session):
        """
        currently,just process it as the unavailable driver
        """
        self.log.debug("currently,just process it as the unavailable driver")
        self._processUnavailableDriver(session)

    
    def _sendRequest(self,peer,action,reporter,extraParam=None,flag=0):

        phone   = peer.getPhone()
        session = peer.getSession()
        params = "From: %s\r\n"%centerID + "To: %s\r\n"%phone
        params +="Action: %s\r\n"%action
        if extraParam:
            params += extraParam
        session.sendRequest("NOTIFY",params,reporter,getCurLocation(),flag)
    
    def getRequestInfo(self):
        return self.serviceInfo + self.passengerInfo
    
    def setPublishStatus(self,status):
        self.status = status
        
    def checkAvailability(self,phone,extraInfo = None,reporter=None):
        
        with AutoLock(self,self.lock):
            session  = self.session.manager.getSessionByPhone(phone)
            peer = session.getPeer()
            if peer.isOnCall():
                self.log.debug("duplicate CA_%s,it is onCall_%d"%(phone,peer.onCallID))
                return
            peer.setOnCall(self.pubID)
            self.status = EPS_CHECKAVAILABILITY
            extraParams = self.getRequestInfo()
            if extraInfo:
                self.poolingID      = extraInfo.get(EDR_POOLINGID)
                self.poolingGroup   = extraInfo.get(EDR_POOLINGGROUP)
                #self.driverGrid     = extraInfo.get(EDR_DRIVER_GRID)
                if self.poolingID:
                    extraParams+="poolingID: %d\r\n"%self.poolingID
                if self.poolingGroup:
                    #extraParams+="poolingID: %d\r\n"%self.pubID
                    extraParams+="poolingIDGroup: %s\r\n"%self.poolingGroup.getPoolingIDStr()
                    for request in self.poolingGroup.requestList:
                        #extraParams+="subID: %d\r\n"%request.pubID
                        extraParams+=request.getRequestInfo()
                        extraParams+="poolingID: %d\r\n"%self.pubID
                        request.setPublishStatus(EPS_CHECKAVAILABILITY)
            else:
                self.poolingGroup = None
                    
            careporter = Reporter(reporter,self._handleCheckAvailabilityResponse,CA_TIMEOUT)
            self.log.debug("begin CA_%s"%phone)
            #peer.setNextTime(time.time() + NEXT_AVAILABLE_TIME_BEFORE_CA)
            self._sendRequest(peer,CSTR_CA,careporter,extraParams)
        
        
if __name__=="__main__":

    pass
   